package net.zoneland.zrdp.common.core.redis;

import java.util.*;
import java.util.concurrent.TimeUnit;

import net.zoneland.zrdp.common.config.ZonevueConfig;
import net.zoneland.zrdp.common.constant.CacheConstants;
import net.zoneland.zrdp.common.exception.BizException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.RedisServerCommands;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * spring redis 工具类
 *
 * @author zonevue
 **/
@Component
public class RedisService {
    private static final Logger LOGGER = LoggerFactory.getLogger(RedisService.class);

    @Resource
    public RedisTemplate<String, Object> redisTemplate;

    @Resource
    private ZonevueConfig zonevueConfig;

    // 递增序列号最大值+1
    private static final long MAX_SEQUENCE = 10000;
    // 流程用序列号key
    private static final String SEQUENCE_KEY = CacheConstants.BUSINESS_KEY + "flow:sequence";

    protected String realRedisKey(final String key) {
        return zonevueConfig.toRedisKey(key);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public void setCacheString(final String key, final String value) {
        redisTemplate.opsForValue().set(realRedisKey(key), value);
    }

    /**
     * 缓存基本的对象，Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     * @param timeout 时间
     * @param timeUnit 时间颗粒度
     */
    public void setCacheString(final String key, final String value, final Integer timeout, final TimeUnit timeUnit) {
        redisTemplate.opsForValue().set(realRedisKey(key), value, timeout, timeUnit);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @return true=设置成功；false=设置失败
     */
    public boolean expire(final String key, final long timeout) {
        return expire(realRedisKey(key), timeout, TimeUnit.SECONDS);
    }

    /**
     * 设置有效时间
     *
     * @param key Redis键
     * @param timeout 超时时间
     * @param unit 时间单位
     * @return true=设置成功；false=设置失败
     */
    public Boolean expire(final String key, final long timeout, final TimeUnit unit) {
        return redisTemplate.expire(realRedisKey(key), timeout, unit);
    }


    /**
     * 获取redis 服务信息
     * @return 服务信息
     */
    public Properties getRedisInfo() {
        return (Properties) redisTemplate.execute((RedisCallback<Object>) RedisServerCommands::info);
    }

    public Properties getRedisCommandStats() {
        return  (Properties) redisTemplate.execute((RedisCallback<Object>) connection -> connection.info("commandstats"));
    }

    public Object getRedisDbSize() {
        return redisTemplate.execute((RedisCallback<Object>) RedisServerCommands::dbSize);
    }
    /**
     * 限流使用
     * @param limitScript 限流脚本
     * @param key redis key
     * @param count 数量
     * @param time 时间
     * @return 数量
     */
    public Long getCacheNumber(final RedisScript<Long> limitScript, final String key, final int count, final int time) {
        final List<String> keys = Collections.singletonList(realRedisKey(key));
        return redisTemplate.execute(limitScript, keys, count, time);
    }

    /**
     * 获取有效时间
     *
     * @param key Redis键
     * @return 有效时间
     */
    public Long getExpire(final String key) {
        return redisTemplate.getExpire(realRedisKey(key));
    }

    /**
     * 判断 key是否存在
     *
     * @param key 键
     * @return true 存在 false不存在
     */
    public Boolean hasKey(final String key) {
        return redisTemplate.hasKey(realRedisKey(key));
    }

    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public String getCacheString(final String key) {
        final ValueOperations<String, Object> operation = redisTemplate.opsForValue();
        final Object value = operation.get(realRedisKey(key));
        return Objects.isNull(value) ? null : value.toString();
    }

    /**
     * 删除单个对象
     *
     * @param key redis key
     */
    public Boolean deleteObject(final String key) {
        return redisTemplate.delete(realRedisKey(key));
    }

    /**
     * 删除集合对象
     *
     * @param collection 多个对象
     * @return 删除是否成功
     */
    public Long deleteObject(final Collection<String> collection) {
        return redisTemplate.delete(collection);
    }

    public Long deleteObjectAll() {
        final Collection<String> cacheKeys = keys("*");
        return deleteObject(cacheKeys);
    }


    /**
     * 获得缓存的基本对象列表
     *
     * @param pattern 字符串前缀
     * @return 对象列表
     */
    public Set<String> keys(final String pattern) {
        final Set<String> rawKeys = redisTemplate.keys(realRedisKey(pattern));
        if (rawKeys == null) {
            return Collections.emptySet();
        }
        return rawKeys;
    }


    /**
     * 获取序列号 0-9999
     *
     * @return 序列号
     */
    public long getNextSequence() {
        final Long currentValue = redisTemplate.opsForValue().increment(realRedisKey(SEQUENCE_KEY));
        // 当redis key 不存在的时候，会自动创建，并设置初始值0，这样increment获取到的值就是1.
        if (currentValue == null) {
            LOGGER.error("redis key {} 获取值时出现问题！！！", SEQUENCE_KEY);
            throw new BizException("redis key " + SEQUENCE_KEY + " 获取值时出现问题！！！");
        }
        return currentValue >= MAX_SEQUENCE ? currentValue % MAX_SEQUENCE : currentValue;
    }

    /**
     * 获取序列号 0000-9999
     * @return 序列号
     */
    public String getNextSequenceStr() {
        return StringUtils.leftPad(String.valueOf(getNextSequence()), 4, '0');
    }

    /**
     * 把序列号设置为0
     */
    public void setSequenceZero() {
        redisTemplate.opsForValue().set(realRedisKey(SEQUENCE_KEY), 0L);
    }
}
